<?php
namespace App\SDK\PingAn\service;

use App\SDK\PingAn\library\JWT;
use App\SDK\PingAn\library\Rsa;
use App\SDK\PingAn\library\Request;
use App\SDK\PingAn\library\Config;

class Upload
{
    private $_rsa;

    private $_jwt;

    public function request($file, $filePathName)
    {
        $result = ['code' => 0, 'msg' => 'ok'];
        $file = realpath($file);
        if (is_file($file)) {
            $config = Config::get();
            $info = pathinfo($file);
            $data = [
                'appId' => $config['app_id'],
                'filePathName' => urlencode($filePathName),
                'container' => '01',
                'md5' => md5_file($file),
                'fileName' => urlencode($info['basename']),
                'fileSize' => filesize($file),
            ];
            $header = $this->getHeader($data);
            $url = $config['url']['upload'];
            $realpath = $info['dirname'] . '\\' . md5($file) . time() . rand(100, 9999) . '.' . $info['extension'];
            copy($file, $realpath);
            $filedata = array('file' => new \CURLFile($realpath));
            $filedata['file']->setPostFilename(basename($file));
            $response = Request::curlPostFile($url, $filedata, $header);
            unlink($realpath);
            if ($GLOBALS['G_HTTP_CODE'] == 200) {
                if ($response) {
                    $data = json_decode($response, true);
                    if ($data && isset($data['code']) && $data['code'] == 200 && isset($data['data']['fileNo'])) {
                        $result['fileNo'] = $data['data']['fileNo'];
                    } else {
                        $result = ['code' => 3, 'msg' => '上传失败'];
                    }
                } else {
                    $result = ['code' => 2, 'msg' => '上传失败'];
                }
            } else {
                $result = ['code' => 3, 'msg' => '上传失败'];
            }
        } else {
            $result = ['code' => 1, 'msg' => '文件不存在'];
        }
        return $result;
    }

    private function getHeader($data) {
        $data = [
            'User-Agent' => 'aop-sdk-php',
            'x-pab-signMethod' => !empty(Config::get('signature_method')) ? Config::get('signature_method') : 'RSA',
            'x-pab-version' => 'PHP-BOAP_1.0.0',
            'Content-Type' => 'multipart/form-data',
            'appId' => $data['appId'],
            'filePathName' => $data['filePathName'],
            'container' => $data['container'],
            'md5' => $data['md5'],
            'fileName' => $data['fileName'],
            'fileSize' => $data['fileSize'],
            'sign' => $this->getSignature($data),
        ];
        return array_map(function ($key, $value){return $key . ': ' . $value;}, array_keys($data), $data);
    }

    private function getSignature($data)
    {
        $signature = '';
        if (Config::get('signature_method') == 'SM2') {
            $signature = $this->jwt()->sign(json_encode($data));
        } else {
            $signature = $this->getSign($data);
        }
        return $signature;
    }

    private function getSign($str) {
        if (is_array($str)) {
            $arr = []; $result = [];
            $this->assemble('', $str, $arr);
            ksort($arr); unset($arr['filePathName']);
            foreach ($arr as $key => $value) {
                $result[] = $key . '=' . $value;
            }
            $str = implode('&', $result) . '&';
        }
        return $this->rsa()->sign($str);
    }

    private function assemble($prev, $data, &$result) {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $this->assemble($prev . (is_string($key) ? $key : ''), $value, $result);
            } else {
                if ($value !== '') {
                    $result[$prev . $key] = $value;
                }
            }
        }
        return $result;
    }

    private function rsa() {
        if (is_null($this->_rsa)) {
            $this->_rsa = new Rsa();
        }
        return $this->_rsa;
    }
    private function jwt()
    {
        if (is_null($this->_jwt)) {
            $this->_jwt = new JWT();
        }
        return $this->_jwt;
    }
}
